/*-------------------------------------------------------------------------
 *
 * postgres.h
 *	  PostgreSQL 服务器 .c 文件的主要包含文件
 *
 * 这应该是 PostgreSQL 后端模块包含的第一个文件。
 * 客户端代码应该包含 postgres_fe.h。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1995, Regents of the University of California
 *
 * src/include/postgres.h
 *
 *-------------------------------------------------------------------------
 */
/*
 *----------------------------------------------------------------
 *	 目录
 *
 *		添加内容到此文件时，请尽量将内容放入相关部分，
 *		或根据需要添加新部分。
 *
 *	  部分	    描述
 *	  -------	------------------------------------------------
 *		1)		变长数据类型（TOAST 支持）
 *		2)		Datum 类型 + 支持宏
 *
 *	 注意
 *
 *	一般来说，此文件应包含在后端环境中广泛需要的
 *	声明，但对后端以外的内容无兴趣。
 *
 *	简单类型定义在 c.h 中，那里与
 *	postgres_fe.h 共享。这样做是因为这些类型定义
 *	被希望与后端进行二进制数据传输的前端模块所需。
 *	此文件中的类型定义应为永远不逃出后端的表示，例如
 *	Datum 或 TOASTed varlena 对象。
 *
 *----------------------------------------------------------------
 */
#ifndef POSTGRES_H
#define POSTGRES_H

#include "c.h"
#include "utils/elog.h"
#include "utils/palloc.h"

/* ----------------------------------------------------------------
 *				第 1 部分：变长数据类型（TOAST 支持）
 * ----------------------------------------------------------------
 */

/*
 * struct varatt_external 是传统的 "TOAST 指针"，即取出
 * 存储在 TOAST 表中以外的 Datum 所需的信息。如果
 * va_extinfo 中存储的外部大小小于 va_rawsize - VARHDRSZ，
 * 则数据被压缩。
 *
 * 此结构不能包含任何填充，因为我们有时会使用
 * memcmp 比较这些指针。
 *
 * 请注意，这些信息在实际元组中以未对齐方式存储，
 * 因此在查看这些字段之前需要从元组复制到本地结构变量中！
 * （我们使用 memcmp 的原因是为了避免仅仅检测两个 TOAST 指针的相等性就不得不这样做...）
 */
typedef struct varatt_external
{
	int32		va_rawsize;		/* 原始数据大小（包括头部） */
	uint32		va_extinfo;		/* 外部保存大小（不含头部）和
								 * 压缩方法 */
	Oid			va_valueid;		/* TOAST 表中值的唯一 ID */
	Oid			va_toastrelid;	/* 包含它的 TOAST 表的 RelID */
}			varatt_external;

/*
 * 这些宏定义了 va_extinfo 的“保存大小”部分。其剩余的
 * 两个高位标识压缩方法。
 */
#define VARLENA_EXTSIZE_BITS	30
#define VARLENA_EXTSIZE_MASK	((1U << VARLENA_EXTSIZE_BITS) - 1)

/*
 * struct varatt_indirect 是一个“TOAST 指针”，表示一个存储在内存中的，
 * 而不是在外部 TOAST 关系中存储的行外 Datum。
 * 此类 Datum 的创建者完全负责确保引用的
 * 存储在引用指针 Datum 存在的整个时间内都能存活。
 *
 * 注意，就像 struct varatt_external，这个结构在任何包含元组中都是存储
 * 未对齐的。
 */
typedef struct varatt_indirect
{
	struct varlena *pointer;	/* 指向内存中 varlena 的指针 */
}			varatt_indirect;

/*
 * struct varatt_expanded 是一个“TOAST 指针”，表示一个存储在内存中的行外
 * Datum，以某种类型特定、并不一定物理连续的格式存储，这种格式方便
 * 用于计算而非存储。此方面的 API，特别是 struct
 * ExpandedObjectHeader 的定义，位于 src/include/utils/expandeddatum.h。
 *
 * 注意，就像 struct varatt_external，这个结构在任何包含元组中都是存储
 * 未对齐的。
 */
typedef struct ExpandedObjectHeader ExpandedObjectHeader;

typedef struct varatt_expanded
{
	ExpandedObjectHeader *eohptr;
} varatt_expanded;

/*
 * 各种“TOAST 指针”datum 的类型标签。VARTAG_ONDISK 的特殊
 * 值来自于对与先前概念的兼容性要求：标签字段是指针 datum 的长度。
 */
typedef enum vartag_external
{
	VARTAG_INDIRECT = 1,
	VARTAG_EXPANDED_RO = 2,
	VARTAG_EXPANDED_RW = 3,
	VARTAG_ONDISK = 18
} vartag_external;

/* 此测试依赖于上述特定标签值 */
#define VARTAG_IS_EXPANDED(tag) \
	(((tag) & ~1) == VARTAG_EXPANDED_RO)

#define VARTAG_SIZE(tag) \
	((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \
	 VARTAG_IS_EXPANDED(tag) ? sizeof(varatt_expanded) : \
	 (tag) == VARTAG_ONDISK ? sizeof(varatt_external) : \
	 TrapMacro(true, "unrecognized TOAST vartag"))

/*
 * 这些结构描述了可能已经 TOAST 的 varlena 对象的头部。
 * 通常，不要直接引用这些结构，而是使用下面的宏。
 *
 * 我们为对齐和未对齐情况使用单独的结构，因为编译器否则可能认为
 * 它可以生成假设对齐的代码，处理 1 字节头的 varlena 的字段。
 */
typedef union
{
	struct						/* 普通 varlena（4 字节长度） */
	{
		uint32		va_header;
		char		va_data[FLEXIBLE_ARRAY_MEMBER];
	}			va_4byte;
	struct						/* 内联压缩格式 */
	{
		uint32		va_header;
		uint32		va_tcinfo;	/* 原始数据大小（不包括头部）和
								 * 压缩方法；见 va_extinfo */
		char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* 压缩数据 */
	}			va_compressed;
} varattrib_4b;

typedef struct
{
	uint8		va_header;
	char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* 数据从这里开始 */
} varattrib_1b;

/* TOAST 指针是 varattrib_1b 的一个子集，具有识别标签字节 */
typedef struct
{
	uint8		va_header;		/* 始终为 0x80 或 0x01 */
	uint8		va_tag;			/* Datum 类型 */
	char		va_data[FLEXIBLE_ARRAY_MEMBER]; /* 类型特定的数据 */
} varattrib_1b_e;

/*
 * 大端机器上的 varlena 头的位布局：
 *
 * 00xxxxxx 四字节长度字，已对齐，未压缩数据（最大 1G）
 * 01xxxxxx 四字节长度字，已对齐，*压缩* 数据（最大 1G）
 * 10000000 一字节长度字，未对齐，TOAST 指针
 * 1xxxxxxx 一字节长度字，未对齐，未压缩数据（最大 126b）
 *
 * 小端机器上的 varlena 头的位布局：
 *
 * xxxxxx00 四字节长度字，已对齐，未压缩数据（最大 1G）
 * xxxxxx10 四字节长度字，已对齐，*压缩* 数据（最大 1G）
 * 00000001 一字节长度字，未对齐，TOAST 指针
 * xxxxxxx1 一字节长度字，未对齐，未压缩数据（最大 126b）
 *
 * “xxx” 位是长度字段（在所有情况下都包括自身）。
 * 在大端情况下，我们屏蔽以提取长度，在小端情况下我们偏移。
 * 注意，在这两种情况下，标志位都在物理第一个字节中。
 * 此外，一字节长度字不可能为零；
 * 这使我们能够将未对齐的 datum 的对齐填充字节与其起始字节区分开来。
 * （我们现在 *要求* 填充字节用零填充！）
 *
 * 在 TOAST 指针中，va_tag 字段（见 varattrib_1b_e）用于辨别
 * 指针 datum 的具体类型和长度。
 */

/*
 * 与字节序相关的宏。这些被视为内部 --- 请使用
 * 外部宏，而不是直接使用这些。
 *
 * 注意：IS_1B 对外部 TOAST 记录为真，但 VARSIZE_1B 对这样的记录将返回 0
 * 因此，您通常应该在检查 IS_1B 之前检查 IS_EXTERNAL。
 */

#ifdef WORDS_BIGENDIAN

#define VARATT_IS_4B(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x80) == 0x00)
#define VARATT_IS_4B_U(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0xC0) == 0x00)
#define VARATT_IS_4B_C(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0xC0) == 0x40)
#define VARATT_IS_1B(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x80) == 0x80)
#define VARATT_IS_1B_E(PTR) \
	((((varattrib_1b *) (PTR))->va_header) == 0x80)
#define VARATT_NOT_PAD_BYTE(PTR) \
	(*((uint8 *) (PTR)) != 0)

/* VARSIZE_4B() 应仅在已知对齐的数据上使用 */
#define VARSIZE_4B(PTR) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header & 0x3FFFFFFF)
#define VARSIZE_1B(PTR) \
	(((varattrib_1b *) (PTR))->va_header & 0x7F)
#define VARTAG_1B_E(PTR) \
	(((varattrib_1b_e *) (PTR))->va_tag)

#define SET_VARSIZE_4B(PTR,len) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header = (len) & 0x3FFFFFFF)
#define SET_VARSIZE_4B_C(PTR,len) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header = ((len) & 0x3FFFFFFF) | 0x40000000)
#define SET_VARSIZE_1B(PTR,len) \
	(((varattrib_1b *) (PTR))->va_header = (len) | 0x80)
#define SET_VARTAG_1B_E(PTR,tag) \
	(((varattrib_1b_e *) (PTR))->va_header = 0x80, \
	 ((varattrib_1b_e *) (PTR))->va_tag = (tag))

#else							/* !WORDS_BIGENDIAN */

#define VARATT_IS_4B(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x00)
#define VARATT_IS_4B_U(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x00)
#define VARATT_IS_4B_C(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x02)
#define VARATT_IS_1B(PTR) \
	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x01)
#define VARATT_IS_1B_E(PTR) \
	((((varattrib_1b *) (PTR))->va_header) == 0x01)
#define VARATT_NOT_PAD_BYTE(PTR) \
	(*((uint8 *) (PTR)) != 0)

/* VARSIZE_4B() 应仅在已知对齐的数据上使用 */
#define VARSIZE_4B(PTR) \
	((((varattrib_4b *) (PTR))->va_4byte.va_header >> 2) & 0x3FFFFFFF)
#define VARSIZE_1B(PTR) \
	((((varattrib_1b *) (PTR))->va_header >> 1) & 0x7F)
#define VARTAG_1B_E(PTR) \
	(((varattrib_1b_e *) (PTR))->va_tag)

#define SET_VARSIZE_4B(PTR,len) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
#define SET_VARSIZE_4B_C(PTR,len) \
	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2) | 0x02)
#define SET_VARSIZE_1B(PTR,len) \
	(((varattrib_1b *) (PTR))->va_header = (((uint8) (len)) << 1) | 0x01)
#define SET_VARTAG_1B_E(PTR,tag) \
	(((varattrib_1b_e *) (PTR))->va_header = 0x01, \
	 ((varattrib_1b_e *) (PTR))->va_tag = (tag))

#endif							/* WORDS_BIGENDIAN */

#define VARDATA_4B(PTR)		(((varattrib_4b *) (PTR))->va_4byte.va_data)
#define VARDATA_4B_C(PTR)	(((varattrib_4b *) (PTR))->va_compressed.va_data)
#define VARDATA_1B(PTR)		(((varattrib_1b *) (PTR))->va_data)
#define VARDATA_1B_E(PTR)	(((varattrib_1b_e *) (PTR))->va_data)

/*
 * 外部可见的 TOAST 宏从这里开始。
 */

#define VARHDRSZ_EXTERNAL		offsetof(varattrib_1b_e, va_data)
#define VARHDRSZ_COMPRESSED		offsetof(varattrib_4b, va_compressed.va_data)
#define VARHDRSZ_SHORT			offsetof(varattrib_1b, va_data)

#define VARATT_SHORT_MAX		0x7F
#define VARATT_CAN_MAKE_SHORT(PTR) \
	(VARATT_IS_4B_U(PTR) && \
	 (VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX)
#define VARATT_CONVERTED_SHORT_SIZE(PTR) \
	(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT)


/*
 * 在对数据对齐毫无察觉的消费者中，调用 PG_DETOAST_DATUM_PACKED()，
 * VARDATA_ANY()，VARSIZE_ANY() 和 VARSIZE_ANY_EXHDR()。在其他地方，调用
 * PG_DETOAST_DATUM()，VARDATA() 和 VARSIZE()。直接访问表示 datum 布局的结构中的
 * int16，int32 或更宽字段需要对齐的数据。memcpy() 是不考虑对齐的，大多数对
 * 数据类型的操作也是如此，例如 text，其布局结构仅包含 char 字段。
 *
 * 组装新 datum 的代码应调用 VARDATA() 和 SET_VARSIZE()。
 * （Datums 的生命周期开始时是未烘焙的。）
 *
 * 此处的其他宏通常应仅由元组组装/拆卸代码和特别希望处理仍然烘焙的 Datums 的代码使用。
 */
#define VARDATA(PTR)						VARDATA_4B(PTR)
#define VARSIZE(PTR)						VARSIZE_4B(PTR)

#define VARSIZE_SHORT(PTR)					VARSIZE_1B(PTR)
#define VARDATA_SHORT(PTR)					VARDATA_1B(PTR)

#define VARTAG_EXTERNAL(PTR)				VARTAG_1B_E(PTR)
#define VARSIZE_EXTERNAL(PTR)				(VARHDRSZ_EXTERNAL + VARTAG_SIZE(VARTAG_EXTERNAL(PTR)))
#define VARDATA_EXTERNAL(PTR)				VARDATA_1B_E(PTR)

#define VARATT_IS_COMPRESSED(PTR)			VARATT_IS_4B_C(PTR)
#define VARATT_IS_EXTERNAL(PTR)				VARATT_IS_1B_E(PTR)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK)
#define VARATT_IS_EXTERNAL_INDIRECT(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT)
#define VARATT_IS_EXTERNAL_EXPANDED_RO(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RO)
#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_EXPANDED_RW)
#define VARATT_IS_EXTERNAL_EXPANDED(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
#define VARATT_IS_EXTERNAL_NON_EXPANDED(PTR) \
	(VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR)))
#define VARATT_IS_SHORT(PTR)				VARATT_IS_1B(PTR)
#define VARATT_IS_EXTENDED(PTR)				(!VARATT_IS_4B_U(PTR))

#define SET_VARSIZE(PTR, len)				SET_VARSIZE_4B(PTR, len)
#define SET_VARSIZE_SHORT(PTR, len)			SET_VARSIZE_1B(PTR, len)
#define SET_VARSIZE_COMPRESSED(PTR, len)	SET_VARSIZE_4B_C(PTR, len)

#define SET_VARTAG_EXTERNAL(PTR, tag)		SET_VARTAG_1B_E(PTR, tag)

#define VARSIZE_ANY(PTR) \
	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR) : \
	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) : \
	  VARSIZE_4B(PTR)))

/* 变长数据的大小，不包括头部 */
#define VARSIZE_ANY_EXHDR(PTR) \
	(VARATT_IS_1B_E(PTR) ? VARSIZE_EXTERNAL(PTR)-VARHDRSZ_EXTERNAL : \
	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR)-VARHDRSZ_SHORT : \
	  VARSIZE_4B(PTR)-VARHDRSZ))

/* 注意：这在外部或行内压缩的 Datum 上将无法工作 */
/* 注意：这将返回一个可能未对齐的指针 */
#define VARDATA_ANY(PTR) \
	 (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))

/* 行内压缩 Datum 的解压缩大小和压缩方法 */
#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) \
	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)

/* 对于外部数据相同，但请注意参数是一个结构 varatt_external */
#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
	((toast_pointer).va_extinfo & VARLENA_EXTSIZE_MASK)
#define VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer) \
	((toast_pointer).va_extinfo >> VARLENA_EXTSIZE_BITS)

#define VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(toast_pointer, len, cm) \
	do { \
		Assert((cm) == TOAST_PGLZ_COMPRESSION_ID || \
			   (cm) == TOAST_LZ4_COMPRESSION_ID); \
		((toast_pointer).va_extinfo = \
			(len) | ((uint32) (cm) << VARLENA_EXTSIZE_BITS)); \
	} while (0)

/*
 * 测试一个外部存储值是否被压缩现在需要
 * 比较 va_extinfo 中存储的大小（外部数据的实际长度）
 * 与 rawsize（原始未压缩数据的大小）。后者包括
 * VARHDRSZ 开销，而前者则不包括。我们从不使用压缩，除非
 * 实际节省空间，因此我们期望要么相等，要么小于。
 */
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
	(VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < \
	 (toast_pointer).va_rawsize - VARHDRSZ)


/* ----------------------------------------------------------------
 *				第2节：Datum 类型 + 支持宏
 * ----------------------------------------------------------------
 */

/*
 * 一个 Datum 包含一个按值传递类型的值或一个指向
 * 按引用传递类型值的指针。因此，我们要求：
 *
 * sizeof(Datum) == sizeof(void *) == 4 或 8
 *
 * 下面的宏和其他类型的类似宏应用于
 * 在 Datum 和适当的 C 类型之间转换。
 */

typedef uintptr_t Datum;

/*
 * NullableDatum 用于需要同时存储 Datum 及其空值状态的地方。
 * 由于更好的空间局部性，虽然由于填充可能浪费更多空间，
 * 但这可能比在单独数组中存储 datums 和空值状态更高效。
 */
typedef struct NullableDatum
{
#define FIELDNO_NULLABLE_DATUM_DATUM 0
	Datum		value;
#define FIELDNO_NULLABLE_DATUM_ISNULL 1
	bool		isnull;
	/* 由于对齐填充，这可以用于标志而不需额外空间 */
} NullableDatum;

#define SIZEOF_DATUM SIZEOF_VOID_P

/*
 * DatumGetBool
 *		返回 datum 的布尔值。
 *
 * 注意：任何非零值将被视为真。
 */

#define DatumGetBool(X) ((bool) ((X) != 0))

/*
 * BoolGetDatum
 *		返回布尔值的 datum 表示。
 *
 * 注意：任何非零值将被视为真。
 */

#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))

/*
 * DatumGetChar
 *		返回 datum 的字符值。
 */

#define DatumGetChar(X) ((char) (X))

/*
 * CharGetDatum
 *		返回字符的 datum 表示。
 */

#define CharGetDatum(X) ((Datum) (X))

/*
 * Int8GetDatum
 *		返回 8 位整数的 datum 表示。
 */

#define Int8GetDatum(X) ((Datum) (X))

/*
 * DatumGetUInt8
 *		返回 datum 的 8 位无符号整数值。
 */

#define DatumGetUInt8(X) ((uint8) (X))

/*
 * UInt8GetDatum
 *		返回 8 位无符号整数的 datum 表示。
 */

#define UInt8GetDatum(X) ((Datum) (X))

/*
 * DatumGetInt16
 *		返回 16 位整数的 datum 值。
 */

#define DatumGetInt16(X) ((int16) (X))

/*
 * Int16GetDatum
 *		返回 16 位整数的 datum 表示。
 */

#define Int16GetDatum(X) ((Datum) (X))

/*
 * DatumGetUInt16
 *		返回 16 位无符号整数的 datum 值。
 */

#define DatumGetUInt16(X) ((uint16) (X))

/*
 * UInt16GetDatum
 *		返回 16 位无符号整数的 datum 表示。
 */

#define UInt16GetDatum(X) ((Datum) (X))

/*
 * DatumGetInt32
 *		返回 32 位整数的 datum 值。
 */

#define DatumGetInt32(X) ((int32) (X))

/*
 * Int32GetDatum
 *		返回 32 位整数的 datum 表示。
 */

#define Int32GetDatum(X) ((Datum) (X))

/*
 * DatumGetUInt32
 *		返回 32 位无符号整数的 datum 值。
 */

#define DatumGetUInt32(X) ((uint32) (X))

/*
 * UInt32GetDatum
 *		返回 32 位无符号整数的 datum 表示。
 */

#define UInt32GetDatum(X) ((Datum) (X))

/*
 * DatumGetObjectId
 *		返回 datum 的对象标识符值。
 */

#define DatumGetObjectId(X) ((Oid) (X))

/*
 * ObjectIdGetDatum
 *		返回对象标识符的 datum 表示。
 */

#define ObjectIdGetDatum(X) ((Datum) (X))

/*
 * DatumGetTransactionId
 *		返回 datum 的事务标识符值。
 */

#define DatumGetTransactionId(X) ((TransactionId) (X))

/*
 * TransactionIdGetDatum
 *		返回事务标识符的 datum 表示。
 */

#define TransactionIdGetDatum(X) ((Datum) (X))

/*
 * MultiXactIdGetDatum
 *		返回多重事务标识符的 datum 表示。
 */

#define MultiXactIdGetDatum(X) ((Datum) (X))

/*
 * DatumGetCommandId
 *		返回 datum 的命令标识符值。
 */

#define DatumGetCommandId(X) ((CommandId) (X))

/*
 * CommandIdGetDatum
 *		返回命令标识符的 datum 表示。
 */

#define CommandIdGetDatum(X) ((Datum) (X))

/*
 * DatumGetPointer
 *		返回 datum 的指针值。
 */

#define DatumGetPointer(X) ((Pointer) (X))

/*
 * PointerGetDatum
 *		返回指针的datum表示。
 */

#define PointerGetDatum(X) ((Datum) (X))

/*
 * DatumGetCString
 *		返回datum的C字符串（以null结尾的字符串）值。
 *
 * 注意：C字符串目前不是一个完整的Postgres类型，
 * 但类型输入函数使用此转换来处理它们的输入。
 */

#define DatumGetCString(X) ((char *) DatumGetPointer(X))

/*
 * CStringGetDatum
 *		返回C字符串（以null结尾的字符串）的datum表示。
 *
 * 注意：C字符串目前不是一个完整的Postgres类型，
 * 但类型输出函数使用此转换来处理它们的输出。
 * 注意：CString是通过引用传递的；调用者必须确保指向的
 * 值具有足够的生命周期。
 */

#define CStringGetDatum(X) PointerGetDatum(X)

/*
 * DatumGetName
 *		返回datum的名称值。
 */

#define DatumGetName(X) ((Name) DatumGetPointer(X))

/*
 * NameGetDatum
 *		返回名称的datum表示。
 *
 * 注意：名称是通过引用传递的；调用者必须确保指向的
 * 值具有足够的生命周期。
 */

#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))

/*
 * DatumGetInt64
 *		返回datum的64位整数值。
 *
 * 注意：此宏隐藏了int64是通过值传递还是通过引用传递。
 */

#ifdef USE_FLOAT8_BYVAL
#define DatumGetInt64(X) ((int64) (X))
#else
#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
#endif

/*
 * Int64GetDatum
 *		返回64位整数的datum表示。
 *
 * 注意：如果int64是通过引用传递，则此函数返回一个对
 * palloc分配空间的引用。
 */

#ifdef USE_FLOAT8_BYVAL
#define Int64GetDatum(X) ((Datum) (X))
#else
extern Datum Int64GetDatum(int64 X);
#endif

/*
 * DatumGetUInt64
 *		返回datum的64位无符号整数值。
 *
 * 注意：此宏隐藏了int64是通过值传递还是通过引用传递。
 */

#ifdef USE_FLOAT8_BYVAL
#define DatumGetUInt64(X) ((uint64) (X))
#else
#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
#endif

/*
 * UInt64GetDatum
 *		返回64位无符号整数的datum表示。
 *
 * 注意：如果int64是通过引用传递，则此函数返回一个对
 * palloc分配空间的引用。
 */

#ifdef USE_FLOAT8_BYVAL
#define UInt64GetDatum(X) ((Datum) (X))
#else
#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
#endif

/*
 * Float <-> Datum转换
 *
 * 当通过值传递时，这些必须实现为内联函数而不是宏，
 * 因为许多机器对int和float函数参数/结果的处理不同；
 * 因此我们需要进行一些奇怪的联合体操作。
 */

/*
 * DatumGetFloat4
 *		返回datum的4字节浮点值。
 */
static inline float4
DatumGetFloat4(Datum X)
{
	union
	{
		int32		value;
		float4		retval;
	}			myunion;

	myunion.value = DatumGetInt32(X);
	return myunion.retval;
}

/*
 * Float4GetDatum
 *		返回4字节浮点数的datum表示。
 */
static inline Datum
Float4GetDatum(float4 X)
{
	union
	{
		float4		value;
		int32		retval;
	}			myunion;

	myunion.value = X;
	return Int32GetDatum(myunion.retval);
}

/*
 * DatumGetFloat8
 *		返回datum的8字节浮点值。
 *
 * 注意：此宏隐藏了float8是通过值传递还是通过引用传递。
 */

#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
	union
	{
		int64		value;
		float8		retval;
	}			myunion;

	myunion.value = DatumGetInt64(X);
	return myunion.retval;
}
#else
#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
#endif

/*
 * Float8GetDatum
 *		返回8字节浮点数的datum表示。
 *
 * 注意：如果float8是通过引用传递，则此函数返回一个对
 * palloc分配空间的引用。
 */

#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
{
	union
	{
		float8		value;
		int64		retval;
	}			myunion;

	myunion.value = X;
	return Int64GetDatum(myunion.retval);
}
#else
extern Datum Float8GetDatum(float8 X);
#endif


/*
 * Int64GetDatumFast
 * Float8GetDatumFast
 *
 * 这些宏旨在允许编写不依赖于
 * int64和float8是否为通过引用传递类型的代码，同时在
 * 它们是时不牺牲性能。参数必须是一个变量，
 * 该变量将在Datum需要的整个时间内存在并具有相同的值。
 * 在通过引用传递的情况下，获取变量的地址用作
 * Datum。在通过值传递的情况下，这些将与非快速
 * 宏相同。
 */

#ifdef USE_FLOAT8_BYVAL
#define Int64GetDatumFast(X)  Int64GetDatum(X)
#define Float8GetDatumFast(X) Float8GetDatum(X)
#else
#define Int64GetDatumFast(X)  PointerGetDatum(&(X))
#define Float8GetDatumFast(X) PointerGetDatum(&(X))
#endif

#ifdef FDD
#define TOKEN_PASSWORD         "password"
#define TOKEN_NULL             "null"
#endif //FDD


#endif							/* POSTGRES_H */
